﻿using CacheExtensions;
using FreeSql;
using FreeSql.Internal;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using SignalRExtensions;
using StackExchange.Redis;
using System.Data.Common;
using System.Reflection;

namespace MultiTenant.FreeSql
{
    public static class MultiTenantBuilderExtensions
    {



        /// <summary>
        /// 注册权限验证服务相关
        /// </summary>
        /// <param name="services"></param>
        /// <param name="section"></param>
        public static void AddDb(this IServiceCollection services
            , IConfigurationSection section)
        {
            var dbConfig = section.Get<DbConfig>() ?? new DbConfig();
            services.Configure<DbConfig>(section);
            DbConfig.DbBegDate = dbConfig.BegDate;
            IdHelper.SetIdGeneratorOptions(dbConfig.WorkerId, (byte)dbConfig.WorkerIdBitLength);
            FreeSqlCloud<string> fsql = new FreeSqlCloud<string>(dbConfig.AppKey);
            fsql.DistributeTrace = log => Console.WriteLine(log.Split('\n')[0].Trim());
            // var dbConfig = PinnedScope.ScopedServices.GetRequiredService<IOptionsMonitor<DbConfig>>();

            fsql.Register("main", () =>
            {
                var db = new FreeSqlBuilder()
                //.UseAutoSyncStructure(env.IsDevelopment() || env.IsStaging())
                .UseConnectionString(dbConfig.DataType, dbConfig.ConnectionString)
                .UseNameConvert(NameConvertType.PascalCaseToUnderscoreWithLower)
                .CreateDatabaseIfNotExistsPgSql(dbConfig.DataType)
                  .UseMonitorCommand(
                    cmd =>
                    {
                        cmd.CommandTimeout = 9999;
                        if (dbConfig.WatchCurd)
                        {
                            var p = "";
                            foreach (DbParameter item in cmd.Parameters)
                            {
                                if (p == "") p += "\r\nParameters：\r\n";
                                p += $"ParameterName:{item.ParameterName},Value:{item.Value}\r\n";
                            }
                            Serilog.Log.Logger.Debug("\r\n*******begin================SQL================**********\r\n" + cmd.CommandText + p + "\r\n*******end================SQL================**********");
                        }
                    })
                .UseNoneCommandParameter(dbConfig.NoneCommandParameter)
                .Build();
                //db.Aop.CommandAfter += ...

                // 全局过滤器只需要在 IFreeSql 初始化处执行一次
                // ITenant 可以是自定义接口，也可以是任何一个包含 TenantId 属性的实体类型，FreeSql 不需要为每个实体类型都设置过滤器（一次即可）
                db.UseJsonMap();

                db.Aop.AuditValue += (s, e) =>
                {
                    DbHelper.AuditValue(e);
                };

                db.Aop.ConfigEntityProperty += (s, e) =>
                {
                    DbHelper.ConfigEntityProperty(s, e);
                };
                db.GlobalFilter.Apply<IBaseSoftDelete>(nameof(IBaseSoftDelete), a => a.IsDeleted == false);

                db.GlobalFilter.ApplyIf<ITenant>(nameof(ITenant), // 过滤器名称
                    () => TenantContext.Tenant != null && TenantContext.Tenant.Id > 0, // 过滤器生效判断
                         a => a.TenantId == TenantContext.Tenant.Id  // 过滤器条件
                    ,true);


                /*    if (dbConfig.SyncStructure)
                    {
                        db.CodeFirst.SyncStructure<Tenant>();
                    }
    */
                return db;
            });

            fsql.EntitySteering = (_, e) =>
            {
                if (e.EntityType.GetCustomAttribute(typeof(SharedTableAttribute), false) != null)
                {
                    e.DBKey = "main";
                }
            };

            services.AddSingleton<IFreeSql>(fsql);
            services.AddScoped<UnitOfWorkManager>();
            services.AddScoped(typeof(IRepositoryDefault<>), typeof(RepositoryDefault<>));
            services.AddScoped(typeof(IRepositoryBase<>), typeof(RepositoryBase<>));
            services.AddScoped(typeof(IRepositoryDefault<,>), typeof(RepositoryDefault<,>));
            services.AddScoped(typeof(IRepositoryBase<,>), typeof(RepositoryBase<,>));
        }

        /// <summary>
        /// Register the SwaggerUI middleware with optional setup action for DI-injected options
        /// </summary>
        public static IApplicationBuilder UseTenantFreeSql(
            this IApplicationBuilder app)
        {


            app.Use(async (context, next) =>
            {
                //if (context.Request.Path.Value?.ToLower().Contains("messagehub") == true)
                //{
                //    await next();
                //}
                //else
                //{
                try
                {
                    //TransactionalAttribute.SetServiceProvider(context.RequestServices);

                    if (TenantContext.Tenant != null && TenantContext.Tenant.Id > 0)
                    {
                        var fsql = context.RequestServices.GetRequiredService<IFreeSql>() as FreeSqlCloud<string>;
                        fsql?.ChangeDb();
                    }
                    //
                    await next();
                }
                finally
                {
                    // 切换回 main 库
                    // DB.Cloud.Change("main");
                }
                //}

            });

            return app;
        }

        public static void AddTenantSignalR(this IServiceCollection services, IConfiguration configuration)
        {

            var config = configuration.GetSection("Cache").Get<CacheConfig>() ?? new CacheConfig();

            var queryPaths = configuration.GetSection("Auth").GetSection("QueryPaths").Get<string[]>();
            if (!(queryPaths != null && queryPaths.Length > 0))
            {
                return;
            }

            if (config.Type == CacheType.Redis && config.Redis != null)
            {
                services.AddSignalR(hubOptions =>
                {
                    hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(15);
                    hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
                }).AddStackExchangeRedis(config.Redis.ConnectionString, options =>
                {
                    options.Configuration.ChannelPrefix = new RedisChannel(config.Prefix, RedisChannel.PatternMode.Auto);
                });
            }
            else
            {
                services.AddSignalR(hubOptions =>
                {
                    hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(15);
                    hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
                });
            }
        }

        public static void MapTenantMessageHub(this WebApplication app)
        {

            var queryPaths = app.Configuration.GetSection("Auth").GetSection("QueryPaths").Get<string[]>();
            if ((queryPaths != null && queryPaths.Length > 0))
            {
                app.MapHub<TenantMessageHub>("messageHub");
                app.MapHub<TenantMessageHub>("{__tenant__}/messageHub");
            }
        }
    }
}
